package com.smart.business.utils;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;

import java.lang.reflect.Field;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.function.Function;

/**
 * 图表工具类，提供获取不同时间粒度折线图数据以及处理饼状图数据的方法。
 */
public class ChartUtil {
    public static <D> List<Map<String, Object>> getYesterLineChartData(Function<QueryWrapper<D>, List<D>> queryFunction, String name) {
        Date today = new Date();
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(today);
        calendar.set(Calendar.HOUR_OF_DAY, 0);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);
        calendar.set(Calendar.MILLISECOND, 0);

        calendar.add(Calendar.DAY_OF_MONTH, -1);
        Date yesterdayStart = calendar.getTime();

        calendar.add(Calendar.DAY_OF_MONTH, 1);
        Date nextDayStart = calendar.getTime();

        List<Map<String, Object>> result = new ArrayList<>();

        calendar.setTime(yesterdayStart);

        while (calendar.getTime().before(nextDayStart)) {
            Date start = calendar.getTime();
            calendar.add(Calendar.HOUR_OF_DAY, 2); // 每2小时为一个区间
            Date end = calendar.getTime();

            if (start.after(today)) {
                break;
            }


            QueryWrapper<D> wrapper = new QueryWrapper<>();
            if (name != null) {
                wrapper.eq("name", name);
            }
            // 设置公司ID
            wrapper.ge("create_organization","COMPANY_012");
            wrapper.ge("pick_time", start).lt("pick_time", end);//表只能这样设计

            List<D> dataList = queryFunction.apply(wrapper);

            double totalConsumption = 0.0;
            for (D data : dataList) {
                try {
                    Field field = data.getClass().getDeclaredField("consume");
                    field.setAccessible(true);
                    totalConsumption += ((Number) field.get(data)).doubleValue();
                } catch (Exception e) {
                    e.printStackTrace();
                }

            }

            Map<String, Object> item = new HashMap<>();
            LocalDateTime startLocal = LocalDateTime.ofInstant(start.toInstant(), ZoneId.systemDefault());
            LocalDateTime endLocal = LocalDateTime.ofInstant(end.toInstant(), ZoneId.systemDefault());
            // 定义 dailyFormatter 并使用它进行日期格式化，这里格式为 "HH:mm"
            DateTimeFormatter dailyFormatter = DateTimeFormatter.ofPattern("HH:mm");
            item.put("time", startLocal.format(dailyFormatter) + "-" + endLocal.format(dailyFormatter));
            item.put("consumption", totalConsumption);
            result.add(item);
        }

        return result;
    }

    /**
     * 获取当天每两小时的能耗数据，用于绘制日折线图。若查询时间为 00:00，则显示昨天的数据。
     *
     * @param queryFunction 一个函数式接口，用于执行查询操作，接收 QueryWrapper 作为参数，返回查询结果列表
     * @param name 用于筛选数据的名称，可为 null
     * @param <D> 查询数据的实体类型
     * @return 包含时间区间和对应能耗值的 Map 列表
     */
    public static <D> List<Map<String, Object>> getDailyLineChartData(Function<QueryWrapper<D>, List<D>> queryFunction, String name) {
        Calendar calendar = Calendar.getInstance();
        // 判断当前时间是否为 00:00
        if (calendar.get(Calendar.HOUR_OF_DAY) == 0 &&
                calendar.get(Calendar.MINUTE) == 0 &&
                calendar.get(Calendar.SECOND) == 0 &&
                calendar.get(Calendar.MILLISECOND) == 0) {
            // 若为 00:00，显示昨天的数据
            calendar.add(Calendar.DAY_OF_MONTH, -1);
        }
        Date today = calendar.getTime();
        calendar.setTime(today);
        // 设置时间为当天 00:00:00
        calendar.set(Calendar.HOUR_OF_DAY, 0);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);
        calendar.set(Calendar.MILLISECOND, 0);

        List<Map<String, Object>> result = new ArrayList<>();

        while (true) {
            Date start = calendar.getTime();
            // 时间区间增加 2 小时
            calendar.add(Calendar.HOUR_OF_DAY, 2);
            Date end = calendar.getTime();

            // 如果起始时间超过当天，则结束循环
            if (start.after(today)) {
                break;
            }

            // 如果结束时间超过当天，则将结束时间设置为当天
            if (end.after(today)) {
                end = today;
            }

            QueryWrapper<D> wrapper = new QueryWrapper<>();
            if (name != null) {
                wrapper.eq("name", name);
            }
            wrapper.ge("create_organization","COMPANY_013");
            // 设置查询条件，筛选 pick_time 在起始时间和结束时间之间的数据
            wrapper.ge("pick_time", start).lt("pick_time", end);

            List<D> dataList = queryFunction.apply(wrapper);

            double totalConsumption = 0.0;
            for (D data : dataList) {
                try {
                    // 通过反射获取 consume 字段的值并累加
                    Field field = data.getClass().getDeclaredField("consume");
                    field.setAccessible(true);
                    totalConsumption += ((Number) field.get(data)).doubleValue();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }

            Map<String, Object> item = new HashMap<>();
            LocalDateTime startLocal = LocalDateTime.ofInstant(start.toInstant(), ZoneId.systemDefault());
            LocalDateTime endLocal = LocalDateTime.ofInstant(end.toInstant(), ZoneId.systemDefault());
            // 定义时间格式化器，格式为 "HH:mm"
            DateTimeFormatter dailyFormatter = DateTimeFormatter.ofPattern("HH:mm");
            item.put("time", startLocal.format(dailyFormatter) + "-" + endLocal.format(dailyFormatter));
            item.put("consumption", totalConsumption);
            result.add(item);
        }
        return result;
    }


    /**
     * 获取本周每天的能耗数据，用于绘制周折线图。
     *
     * @param queryFunction 一个函数式接口，用于执行查询操作，接收 QueryWrapper 作为参数，返回查询结果列表
     * @param name          用于筛选数据的名称，可为 null
     * @param <D>           查询数据的实体类型
     * @return 包含星期几和对应能耗值的 Map 列表
     */
    public static <D> List<Map<String, Object>> getWeeklyLineChartData(Function<QueryWrapper<D>, List<D>> queryFunction, String name) {
        // 获取当前日期
        Date today = new Date();
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(today);
        // 设置时间为本周周日 00:00:00
        calendar.set(Calendar.DAY_OF_WEEK, Calendar.SUNDAY);
        calendar.set(Calendar.HOUR_OF_DAY, 0);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);
        calendar.set(Calendar.MILLISECOND, 0);

        List<Map<String, Object>> result = new ArrayList<>();
        Calendar todayCalendar = Calendar.getInstance();
        todayCalendar.setTime(today);

        while (true) {
            Date start = calendar.getTime();
            // 时间区间增加 1 天
            calendar.add(Calendar.DAY_OF_WEEK, 1);
            Date end = calendar.getTime();

            // 如果起始时间超过当天，则结束循环
            if (start.after(today)) {
                break;
            }

            // 如果结束时间超过当天，则将结束时间设置为当天
            if (end.after(today)) {
                end = today;
            }

            QueryWrapper<D> wrapper = new QueryWrapper<>();
            if (name != null) {
                wrapper.eq("name", name);
            }
            wrapper.ge("create_organization","COMPANY_012");
            // 设置查询条件，筛选 pick_time 在起始时间和结束时间之间的数据
            wrapper.ge("pick_time", start)
                    .lt("pick_time", end);

            List<D> dataList = queryFunction.apply(wrapper);

            double totalConsumption = 0.0;
            for (D data : dataList) {
                try {
                    // 通过反射获取 consume 字段的值并累加
                    Field field = data.getClass().getDeclaredField("consume");
                    field.setAccessible(true);
                    totalConsumption += ((Number) field.get(data)).doubleValue();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }

            Map<String, Object> item = new HashMap<>();
            LocalDateTime startLocal = LocalDateTime.ofInstant(start.toInstant(), ZoneId.systemDefault());
            // 定义时间格式化器，格式为 "E"（星期几）
            DateTimeFormatter weekFormatter = DateTimeFormatter.ofPattern("E");
            item.put("time", startLocal.format(weekFormatter));
            item.put("consumption", totalConsumption);
            result.add(item);
        }
        return result;
    }

    /**
     * 获取本月每天的能耗数据，用于绘制月折线图。
     *
     * @param queryFunction 一个函数式接口，用于执行查询操作，接收 QueryWrapper 作为参数，返回查询结果列表
     * @param name          用于筛选数据的名称，可为 null
     * @param <D>           查询数据的实体类型
     * @return 包含日期和对应能耗值的 Map 列表
     */
    public static <D> List<Map<String, Object>> getMonthlyLineChartData(Function<QueryWrapper<D>, List<D>> queryFunction, String name) {
        // 获取当前日期
        Date today = new Date();
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(today);
        // 设置时间为本月 1 号 00:00:00
        calendar.set(Calendar.DAY_OF_MONTH, 1);
        calendar.set(Calendar.HOUR_OF_DAY, 0);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);
        calendar.set(Calendar.MILLISECOND, 0);

        List<Map<String, Object>> result = new ArrayList<>();
        Calendar todayCalendar = Calendar.getInstance();
        todayCalendar.setTime(today);

        while (true) {
            Date start = calendar.getTime();
            // 时间区间增加 1 天
            calendar.add(Calendar.DAY_OF_MONTH, 1);
            Date end = calendar.getTime();

            // 如果起始时间超过当天，则结束循环
            if (start.after(today)) {
                break;
            }

            // 如果结束时间超过当天，则将结束时间设置为当天
            if (end.after(today)) {
                end = today;
            }

            QueryWrapper<D> wrapper = new QueryWrapper<>();
            if (name != null) {
                wrapper.eq("name", name);
            }
            // 添加公司 ID 筛选条件
            wrapper.eq("create_organization", "COMPANY_012");
            // 设置查询条件，筛选 pick_time 在起始时间和结束时间之间的数据
            wrapper.ge("pick_time", start)
                    .lt("pick_time", end);

            List<D> dataList = queryFunction.apply(wrapper);

            double totalConsumption = 0.0;
            for (D data : dataList) {
                try {
                    // 通过反射获取 consume 字段的值并累加
                    Field field = data.getClass().getDeclaredField("consume");
                    field.setAccessible(true);
                    totalConsumption += ((Number) field.get(data)).doubleValue();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }

            Map<String, Object> item = new HashMap<>();
            LocalDateTime startLocal = LocalDateTime.ofInstant(start.toInstant(), ZoneId.systemDefault());
            // 定义时间格式化器，格式为 "yyyy-MM-dd"
            DateTimeFormatter monthlyFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
            item.put("time", startLocal.format(monthlyFormatter));
            item.put("consumption", totalConsumption);
            result.add(item);
        }
        return result;
    }

    /**
     * 计算总耗电值。
     *
     * @param rawData 包含原始数据的 Map 列表，每个 Map 包含 "total_consume" 字段
     * @return 总耗电值
     */
    public static double calculateTotalConsume(List<Map<String, Object>> rawData) {
        double totalConsume = 0.0;
        for (Map<String, Object> data : rawData) {
            totalConsume += ((Number) data.get("total_consume")).doubleValue();
        }
        return totalConsume;
    }

    /**
     * 计算每个名称的能耗值在总能耗中的百分比。
     *
     * @param rawData      包含原始数据的 Map 列表，每个 Map 包含 "name" 和 "total_consume" 字段
     * @param totalConsume 总耗电值
     * @return 包含处理后数据的 Map 列表，每个 Map 包含 "name"、"total_consume" 和 "percentage" 字段
     */
    public static List<Map<String, Object>> calculatePercentage(List<Map<String, Object>> rawData, double totalConsume) {
        List<Map<String, Object>> result = new ArrayList<>();
        for (Map<String, Object> data : rawData) {
            Map<String, Object> item = new HashMap<>();
            item.put("name", data.get("name"));
            item.put("total_consume", data.get("total_consume"));
            if (totalConsume > 0) {
                double percentage = ((Number) data.get("total_consume")).doubleValue() / totalConsume * 100;
                item.put("percentage", percentage);
            } else {
                item.put("percentage", 0.0);
            }
            result.add(item);
        }
        return result;
    }

    /**
     * 计算饼状图中“其它”的消耗值，并添加到结果列表中，同时计算百分比
     * @param rawData 原始数据列表
     * @param name 层级名称
     * @param consumptionGetter 获取消耗值的函数
     * @return 包含百分比的结果列表
     */
    public static <T> List<Map<String, Object>> pieChartData(List<T> rawData, String name,
                                                             Function<T, Double> consumptionGetter,
                                                             Function<String, Double> totalConsumptionGetter) {
        List<Map<String, Object>> result = new ArrayList<>();
        double totalConsume = 0.0;
        double knownConsumption = 0.0;

        // 遍历原始数据，计算已知消耗值
        for (T item : rawData) {
            Double consumption = consumptionGetter.apply(item);
            if (consumption != null && consumption > 0) {
                totalConsume += consumption;
                knownConsumption += consumption;
                Map<String, Object> mapItem = new HashMap<>();
                mapItem.put("name", item);
                mapItem.put("value", consumption);
                result.add(mapItem);
            }
        }

        // 获取当前层级的总能耗
        Double totalEnergy = totalConsumptionGetter.apply(name);
        if (totalEnergy != null && totalEnergy > 0) {
            totalConsume = totalEnergy;
        }

        // 计算“其它”部分的能耗
        double otherConsumption = totalConsume - knownConsumption;
        otherConsumption = Math.round(otherConsumption * 100) / 100.0;
        if (otherConsumption > 0) {
            Map<String, Object> otherItem = new HashMap<>();
            otherItem.put("name", "其它");
            otherItem.put("value", otherConsumption);
            result.add(otherItem);
        }

        // 计算百分比
        for (Map<String, Object> item : result) {
            Double value = (Double) item.get("value");
            double percentage = Math.round(value / totalConsume * 10000) / 100.0;
            item.put("percentage", percentage + "%");
        }

        return result;
    }




}